#include <iostream>
using namespace std;

/*
    泛型：通用类型,模板就是一种泛型(通用类型)
      泛型编程：只要程序员写代码的时候用到了模板，我们就说这个程序员的代码中采用了泛型编程技术
                       旨在编写独立于数据类型的代码(不必考虑模板究竟是什么类型)

    引入模板模型（泛型编程）
    1.前面学习过函数重载，我们定义了好多个版本的add
        int add（int n1.int m2）;
        double add(double n1,double m2);
        观察发现：程序员需要动手写很多不同版本的add，太啰嗦，要针对不同类型不同个数的参数定义不同版本的add
    提出改进方法: 这些add旧是返回值类型，形参类型有所差异
        我打算把返回值类型，形参类型用变量表示出来（把返回值类型，形参类型做个抽象）
        T add（T n1,T n2）;
        T是万能类型，只要是c++合法的数据类型，T都可以表示
        计算机科学家需要给这个技术取个专业术语--》 把T叫做模板
    总结：模板的本质，就是类型参数

    2.如何定义使用模板呢？
      语法规则
      template <typename T>  // 声明一个模板参数，参数名字叫做T
                             // template 是C++关键字，专门声明模板参数
                             // typename 是C++关键字，专门定义模板参数的名字
                             // 模板参数的名字，可以任意取名（通常来说，大家都是使用T作为参数名字）
      T add(T n1,T n2) // 定义add这个函数，该函数使用了刚才声明的模板参数T作为返回值
                       // 模板函数: 只要一个函数使用了模板作为返回值或者形参，那么这个函数就是模板函数

    3.模板函数的底层原理
    编译器会依据程序员传递的实参类型，自动生成对应版本的add调用执行源码
*/

template <typename T> // 声明一个模板参数，参数名字叫做T
// template <class T>    // 声明一个模板参数，参数名字叫做T           也可以用这个 历史原因：C++早期版本没有typename
T add(T n1, T n2) // 定义add这个函数，该函数使用了刚才声明的模板参数T作为返回值
{
    return n1 + n2;
}

int main()
{
    int a = 10, b = 20;
    double c = 10.51, d = 20.5;

    cout << "a + b = " << add(a, b) << endl; // 编译器会自动生成一个add函数，函数的返回值类型是int，形参类型是int
    cout << "c + d = " << add(c, d) << endl; // 编译器会自动生成一个add函数，函数的返回值类型是double，形参类型是double

    // 证明模板函数的底层原理
    // add(a, c); // 错误，因为a和c类型不一致，编译器不知道该生成哪个版本的add函数
    return 0;
}
